/***************************************************************
*  Copyright (c) 2007 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.search.flickr;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import net.groupme.controller.model.ImageSearchResult;
import net.groupme.controller.model.SearchResult;
import net.groupme.exceptions.search.SearchEngineException;
import net.groupme.model.MODEL_CONSTANTS;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.SAXException;

import com.aetrion.flickr.Flickr;
import com.aetrion.flickr.FlickrException;
import com.aetrion.flickr.photos.Photo;
import com.aetrion.flickr.photos.PhotoList;
import com.aetrion.flickr.photos.PhotosInterface;
import com.aetrion.flickr.photos.SearchParameters;

/**
 * created on Jun 25, 2007
 * 
 * This class provides methos to search for photos at <a href="http://flickr.com">flickr</a>. 
 * 
 * @author Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by: $Author: mischa $
 * 
 * @version $Revision: 1.13 $ $Date: 2008-07-20 17:53:53 $
 */
public class FlickrSearchEngine {
	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	/**
	 * Returns an Array of {@link ImageSearchResult} instances that suit the given tag query 
	 * @param tags a string containing tags that have to appear at each image
	 * @param numberOfMaximumResults the maximum number of results
	 * @return a List of {@link SearchResult} instances that suit the given tag query 
	 * @throws SearchEngineException thrown if problems occur on queryinf the flickr api 
	 */
	@SuppressWarnings("unchecked")
	public List<SearchResult> searchForFlickrPhotos(String tags, int numberOfMaximumResults) throws SearchEngineException{
		List<SearchResult> resultList = new ArrayList<SearchResult>();
		PhotoList flickrResult = null;
		try {
			flickrResult = getPhotosByTag(extractSingleTags(tags), true, numberOfMaximumResults);
		} catch (IOException e) {
			throw new SearchEngineException("Problems on contacting the flickr api.", e);
		} catch (SAXException e) {
			throw new SearchEngineException("Problems on reading the result of the flickr api call.", e);
		} catch (FlickrException e) {
			throw new SearchEngineException("Internal problems of the Flickr Java API.", e);
		}
		ListIterator<Photo> photos = flickrResult.listIterator(); int counter = 0;
		while (photos.hasNext()) {
			Photo photo = photos.next();
			ImageSearchResult image = new ImageSearchResult();
			image.setName(photo.getTitle());
			image.setUrl(photo.getMediumUrl());
			image.setDescription(photo.getDescription());
			image.setUrlOfThumbnail(photo.getThumbnailUrl());
			if (photo.getDateTaken() != null) {
				image.setDateCreated(photo.getDateTaken().toString());
			}
			image.setFlickrTags(photo.getTags());
			if (photo.getOwner() != null) {
				if(photo.getOwner().getUsername() != null){
					image.setOwner(photo.getOwner().getUsername());
				}else{
					image.setOwner(photo.getOwner().getId());
				}
			}
			if (photo.getGeoData() != null) {
				image.setGeoDataLatitude(photo.getGeoData().getLatitude());
				image.setGeoDataLongitude(photo.getGeoData().getLongitude());
			}
			image.setPublisher(FLICKR_VOCABULARY.FLICKR_URL);
			image.setResultID("imageresult" + counter);
			image.setType(MODEL_CONSTANTS.RESOURCE_TYPE_IMAGE);
			counter++;

			resultList.add(image);
		}
		return resultList;
	}

	/**
	 * Extracts single tags and returns them as Array.
	 * @param tags tag string (e.g. "'Web 2.0' Ajax DWR")
	 * @return tag string as array.
	 */
	protected String[] extractSingleTags(String tags){
		List<String> tagList = new ArrayList<String>(); 
		String actual = tags.trim();
		while(actual.length() > 0){
			//extract phrase
			if(actual.charAt(0) == '"'){
				actual = actual.substring(1);
				if(actual.indexOf("\"") > 0){
					tagList.add(actual.substring(0, actual.indexOf("\"")));
					actual = actual.substring(actual.indexOf("\"") +1).trim();
				}
			}else if (actual.indexOf(" ") > 0){
				tagList.add(actual.substring(0, actual.indexOf(" ")));
				actual = actual.substring(actual.indexOf(" ") +1).trim();
			}else{
				tagList.add(actual);
				String[] result = new String[tagList.size()];
				return tagList.toArray(result);
			}
		}
		tagList.add(actual);
		String[] result = new String[tagList.size()];
		return tagList.toArray(result);
	}
	/**
	 * This method returns an Interface to search for photos.
	 * @return an Interface to search for photos.
	 */
	private PhotosInterface getPhotoInterface(){
		String apiKey = FLICKR_VOCABULARY.FLICKR_API_KEY;
		Flickr flickr = new Flickr(apiKey);
		return flickr.getPhotosInterface();
	}
	/**
	 * Returns a list of photos that suit given tags.
	 * @param tags tags like <i>Sport</i>, <i>Hannover</i>, ...
	 * @param hasToSuitAllTags if this parameter is <code>true</code> then only those photos are returned which 
	 * suit all the given tags otherwise those photos are returned that suit any tag.
	 * @param numberOfMaximumResults the maximum number of results/photos
	 * 
	 * @return a list of suiting photos
	 * @throws FlickrException Problem with Flickr API
	 * @throws SAXException Problem gduring generating the request or accessing the results
	 * @throws IOException some other problems
	 */
	@SuppressWarnings("unchecked")
	private PhotoList getPhotosByTag(String[] tags, boolean hasToSuitAllTags, int numberOfMaximumResults) throws IOException, SAXException, FlickrException{
		PhotosInterface photoInterface = getPhotoInterface();
		SearchParameters parameters = new SearchParameters();
		parameters.setTags(tags);
		if(hasToSuitAllTags){
			parameters.setTagMode(FLICKR_VOCABULARY.SEARCH_FOR_ALL_TAGS);
		}else{
			parameters.setTagMode(FLICKR_VOCABULARY.SEARCH_FOR_ANY_TAGS);
		}
		//gather Flickr-specific metadata:
		parameters.setExtras(true);
		parameters.setExtrasDateTaken(true);
		parameters.setExtrasDateUpload(true);
		parameters.setExtrasOwnerName(true);
		parameters.setSort(SearchParameters.INTERESTINGNESS_DESC);
		PhotoList resultPhotoList = new PhotoList();
		PhotoList photoList =  new PhotoList();
		int numberOfResultsPerPage = FLICKR_VOCABULARY.MAXIMUM_NUMBER_OF_RESULTS_PER_REQUEST;
		if(numberOfMaximumResults < numberOfResultsPerPage){
			numberOfResultsPerPage = numberOfMaximumResults;
		}
		int numberOfAddedResults = 0;
		int actualResultPage = 1;
		while(numberOfAddedResults <= numberOfMaximumResults && (actualResultPage == 1 || actualResultPage <= photoList.getPages())){
			photoList = photoInterface.search(parameters, numberOfResultsPerPage, actualResultPage);
			ListIterator it = photoList.listIterator();
			while(it.hasNext() && numberOfAddedResults <= numberOfMaximumResults){
				Photo photo = (Photo) it.next();
				//TODO: Als Suchergebnis hat ein Photo nur sehr wenige Eigenschaften, daher muessen wir irgendwie an weitere Eigenschaften rankommen (photoInterface.getInf(photo.getId(), "") & Co. funktionieren leider nicht...
				resultPhotoList.add(photo);
				numberOfAddedResults++;
			}
			actualResultPage++;
		}
		
		return resultPhotoList;
	}
	
}
